//*********************************************************
//
//    Copyright (c) Microsoft. All rights reserved.
//    This code is licensed under the Apache License, Version 2.0.
//    THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
//    ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
//    IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
//    PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
//
//*********************************************************

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Text;

namespace Microsoft.Research.DataLayer
{
    partial class Machine
    {

        /// <summary>
        /// Gets or sets the last poll time.
        /// </summary>
        /// <value>The last poll time.</value>
        public System.DateTime LastPollTime
        {
            get
            {
                return this.HealthChecks[0].LastPollTime;
            }
            set
            {
                this.HealthChecks[0].LastPollTime = value;
            }
        }

        /// <summary>
        /// Gets or sets the executor status message.
        /// </summary>
        /// <value>The executor status message.</value>
        public System.String ExecutorStatusMessage
        {
            get
            {
                return this.HealthChecks[0].ExecutorStatusMessage;
            }
            set
            {
                this.HealthChecks[0].ExecutorStatusMessage = value;
            }
        }

        /// <summary>
        /// Gets or sets the scheduler status message.
        /// </summary>
        /// <value>The scheduler status message.</value>
        public System.String SchedulerStatusMessage
        {
            get
            {
                return this.HealthChecks[0].SchedulerStatusMessage;
            }
            set
            {
                this.HealthChecks[0].SchedulerStatusMessage = value;
            }
        }

        /// <summary>
        /// Gets or sets the executor status.
        /// </summary>
        /// <value>The status.</value>
        public MachineStatus Status
        {
            get
            {
                return this.HealthChecks[0].ExecutorStatus;
            }
            set
            {
                this.HealthChecks[0].ExecutorStatus = value;
            }
        }

        /// <summary>
        /// Gets or sets the scheduler status.
        /// </summary>
        /// <value>The scheduler status.</value>
        public SchedulerStatus SchedulerStatus
        {
            get
            {
                return this.HealthChecks[0].SchedulerStatus;
            }
            set
            {
                this.HealthChecks[0].SchedulerStatus = value;
            }
        }

        public static Machine GetCreateForCluster(string fullname, Connection c)
        {
            Collection<Machine> machineCollection = null;
            try
            {
                machineCollection = CommonSearches.GetMachineByName(c, false, fullname);
                return machineCollection[0];
            }
            catch (RecordNotFoundException)
            {
                Machine m = Machine.Create(fullname, c);
                HealthCheck.Create(m, c);
                m.IsInteractiveExecution = false;
                m.Type = MachineType.HPCCluster;
                m.Availability = ExecutorAvailabiltiy.Online;
                m.Save();
                return m;
            }
        }

        /// <summary>
        /// Returns a list of all the jobs on the server with the
        /// status as Waiting.
        /// </summary>
        /// <param name="server">Machine on which the jobs would execute.</param>
        /// <returns>List of jobs to be executed.</returns>
        public List<Job> GetJobsToRun()
        {
            List<Job> wfJobs = new List<Job>();
            try
            {
                Job.ISearch searchQuery = Job.ISearch.Create();
                Job.ISearch.ISearchClause machineClause = Job.ISearch.Fields.Machine.CreateSearchClause(RelationFieldBase.Condition.Equals, this);
                //At least 2 search clauses required to use OR with array,
                //If count is less than 2 use only the server clause initialized at the begining
                if (this.Children.Count >= 1)
                {
                    Job.ISearch.ISearchClause[] machineQuery = new Job.ISearch.ISearchClause[this.Children.Count + 1];
                    int computeNodeIndex = 0;
                    //Add all the children to query so that we can process all the jobs in c
                    for (; computeNodeIndex < this.Children.Count; computeNodeIndex++)
                    {
                        machineQuery[computeNodeIndex] = Job.ISearch.Machine(RelationFieldBase.Condition.Equals, this.Children[computeNodeIndex]);
                    }
                    machineQuery[computeNodeIndex] = Job.ISearch.Fields.Machine.CreateSearchClause(RelationFieldBase.Condition.Equals, this);
                    machineClause = Job.ISearch.OR(machineQuery);
                }
                searchQuery.Query = Job.ISearch.AND(
                          machineClause,
                          Job.ISearch.Status(EnumFieldBase.Condition.Equals, JobStatus.Waiting),
                          Job.ISearch.IsTemplate(BooleanField.Condition.Equals, false));
                wfJobs = Job.Search(searchQuery, this.Conn);
            }
            catch (RecordNotFoundException)
            {
            }
            return wfJobs;
        }

        /// <summary>
        /// Method captures all the jobs in running or pending status
        /// </summary>
        /// <param name="server"></param>
        /// <param name="connection"></param>
        /// <returns></returns>
        public List<Job> GetJobsToVerify()
        {
            List<Job> wfJobs = new List<Job>();

            try
            {
                Job.ISearch searchQuery = Job.ISearch.Create();
                Job.ISearch.ISearchClause machineClause = Job.ISearch.Fields.Machine.CreateSearchClause(RelationFieldBase.Condition.Equals, this);
                //At least 2 search clauses required to use OR with array,
                //If count is less than 2 use only the server clause initialized at the begining otherwise update the clause with array
                if (this.Children.Count >= 1)
                {
                    Job.ISearch.ISearchClause[] machineQuery = new Job.ISearch.ISearchClause[this.Children.Count + 1];
                    int computeNodeIndex = 0;
                    //Add all the children to query so that we can process all the jobs in c
                    for (; computeNodeIndex < this.Children.Count; computeNodeIndex++)
                    {
                        machineQuery[computeNodeIndex] = Job.ISearch.Machine(RelationFieldBase.Condition.Equals, this.Children[computeNodeIndex]);
                    }
                    machineQuery[computeNodeIndex] = Job.ISearch.Fields.Machine.CreateSearchClause(RelationFieldBase.Condition.Equals, this);
                    machineClause = Job.ISearch.OR(machineQuery);
                }

                searchQuery.Query = Job.ISearch.AND(
                        Job.ISearch.IsTemplate(BooleanField.Condition.Equals, false),
                        Job.ISearch.OR(
                            Job.ISearch.Fields.Status.CreateSearchClause(EnumFieldBase.Condition.Equals, JobStatus.StartPending),
                            Job.ISearch.Fields.Status.CreateSearchClause(EnumFieldBase.Condition.Equals, JobStatus.Running)
                            ),
                        machineClause
                        );

                wfJobs = Job.Search(searchQuery, this.Conn);
            }
            catch (RecordNotFoundException)
            {
            }
            return wfJobs;
        }

        /// <summary>
        /// Create a virtual machine.
        /// </summary>
        /// <param name="machineName">Virtual machine name.</param>
        /// <param name="registryConnection">Registry connection.</param>
        /// <returns>Virtual machine instance which was created.</returns>
        public static Machine CreateVirtualMachine(string machineName, Connection registryConnection)
        {
            Machine virtualMachine = Machine.Create(machineName, MachineType.Virtual, registryConnection);
            virtualMachine.Availability = ExecutorAvailabiltiy.Online;
            virtualMachine.IsInteractiveExecution = false;
            virtualMachine.Save();

            HealthCheck healthCheck = HealthCheck.Create(virtualMachine, registryConnection);
            healthCheck.ExecutorStatus = MachineStatus.Running;
            healthCheck.SchedulerStatus = SchedulerStatus.Running;
            healthCheck.LastPollTime = DateTime.UtcNow;

            healthCheck.Save();

            return virtualMachine;
        }

        #region CommonSearches
        public sealed class CommonSearches
        {
            private CommonSearches()
            {
            }

            /// <summary>
            /// Retreive the online nodes.
            /// </summary>
            /// <param name="connection">The registry connection.</param>
            /// <param name="isInteractive">isInteractive flag</param>
            /// <returns>Collection of online nodes present in the registry.</returns>
            public static Collection<Machine> GetOnlineMachines(Connection connection, bool isInteractive)
            {
                #region Validation of Arguments
                if (null == connection)
                {
                    throw new ArgumentNullException("connection");
                }
                #endregion

                Collection<Machine> availableMachines = null;
                try
                {
                    Machine.ISearch searchQuery = Machine.ISearch.Create();
                    searchQuery.Query = Machine.ISearch.AND(
                        Machine.ISearch.Availability(EnumFieldBase.Condition.Equals, ExecutorAvailabiltiy.Online),
                        Machine.ISearch.IsDeleted(BooleanField.Condition.Equals, false),
                        Machine.ISearch.Type(EnumFieldBase.Condition.NotEquals, MachineType.Virtual),
                        Machine.ISearch.IsInteractiveExecution(BooleanField.Condition.Equals, isInteractive));
                    availableMachines = new Collection<Machine>(Machine.Search(searchQuery, connection));
                }
                catch (RecordNotFoundException)
                {
                    throw;
                }
                return availableMachines;
            }

            /// <summary>
            /// Retrieve the available nodes.
            /// </summary>
            /// <param name="connection">The registry connection.</param>
            /// <returns>Collection of all the nodes present in the registry.</returns>
            public static Collection<Machine> GetAllNodes(Connection connection)
            {
                #region Validation of Arguments
                if (null == connection)
                {
                    throw new ArgumentNullException("connection");
                }
                #endregion

                Collection<Machine> machines = null;
                try
                {
                    Machine.ISearch searchQuery = Machine.ISearch.Create();
                    searchQuery.Query = Machine.ISearch.IsDeleted(BooleanField.Condition.Equals, false);
                    machines = new Collection<Machine>(Machine.Search(searchQuery, connection));
                }
                catch (RecordNotFoundException)
                {
                    throw;
                }
                return machines;
            }

            /// <summary>
            /// Retreive the online nodes.
            /// </summary>
            /// <param name="connection">The registry connection.</param>
            /// <param name="isInteractive">isInteractive flag.</param>
            /// <param name="hostName">Name of the Host.</param>
            /// <returns>Collection of online nodes present in the registry.</returns>
            public static Collection<Machine> GetMachineByName(Connection connection, bool isInteractive, string hostName)
            {
                #region Validation of Arguments
                if (null == connection)
                {
                    throw new ArgumentNullException("connection");
                }
                if (string.IsNullOrEmpty(hostName))
                {
                    throw new ArgumentNullException("hostName");
                }
                #endregion

                Collection<Machine> availableMachines = null;
                try
                {
                    Machine.ISearch searchQuery = Machine.ISearch.Create();
                    searchQuery.Query = Machine.ISearch.AND(
                        Machine.ISearch.IsDeleted(BooleanField.Condition.Equals, false),
                        Machine.ISearch.IsInteractiveExecution(BooleanField.Condition.Equals, isInteractive),
                        Machine.ISearch.Type(EnumFieldBase.Condition.NotEquals, MachineType.Virtual),
                        Machine.ISearch.Name(StringField.Condition.Equals, hostName));
                    availableMachines = new Collection<Machine>(Machine.Search(searchQuery, connection));
                }
                catch (RecordNotFoundException)
                {
                    throw;
                }
                return availableMachines;
            }

            /// <summary>
            /// Retreive the virtual node related.
            /// </summary>
            /// <param name="hostName">Name of the Host.</param>
            /// <param name="connection">The registry connection.</param>
            /// <returns>Collection of online nodes present in the registry.</returns>
            public static Machine GetVirtualMachineByName(string hostName, Connection connection)
            {
                #region Validation of Arguments
                if (null == connection)
                {
                    throw new ArgumentNullException("connection");
                }
                if (string.IsNullOrEmpty(hostName))
                {
                    throw new ArgumentNullException("hostName");
                }
                #endregion

                Machine virtualMachine = null;

                try
                {
                    Collection<Machine> availableMachines = null;
                    Machine.ISearch searchQuery = Machine.ISearch.Create();
                    searchQuery.Query = Machine.ISearch.AND(
                        Machine.ISearch.IsDeleted(BooleanField.Condition.Equals, false),
                        Machine.ISearch.Type(EnumFieldBase.Condition.Equals, MachineType.Virtual),
                        Machine.ISearch.Name(StringField.Condition.Equals, hostName));
                    availableMachines = new Collection<Machine>(Machine.Search(searchQuery, connection));
                    virtualMachine = availableMachines[0];
                }
                catch (RecordNotFoundException)
                {
                    // Ignore: If recored is not present then return null.
                }

                return virtualMachine;
            }

            /// <summary>
            /// Retreive the nodes.
            /// </summary>
            /// <param name="connection">The registry connection.</param>
            /// <param name="hostName">Name of the Host.</param>
            /// <returns>Collection of online nodes present in the registry.</returns>
            public static Collection<Machine> GetAllMachinesByName(Connection connection, string hostName)
            {
                #region Validation of Arguments
                if (null == connection)
                {
                    throw new ArgumentNullException("connection");
                }
                if (string.IsNullOrEmpty(hostName))
                {
                    throw new ArgumentNullException("hostName");
                }
                #endregion

                Collection<Machine> availableMachines = null;
                try
                {
                    Machine.ISearch searchQuery = Machine.ISearch.Create();
                    searchQuery.Query = Machine.ISearch.AND(
                        Machine.ISearch.IsDeleted(BooleanField.Condition.Equals, false),
                        Machine.ISearch.Name(StringField.Condition.Equals, hostName));
                    availableMachines = new Collection<Machine>(Machine.Search(searchQuery, connection));
                }
                catch (RecordNotFoundException)
                {
                    throw;
                }
                return availableMachines;
            }
        }
        #endregion
    }
}
